#include "Shader.h"

Shader::Shader(const std::string& filePath):m_FilePath(filePath)
{
    ShaderProgramSource source = ParseShader(m_FilePath);
    // std::cout << "VERTEX:" << std::endl;
    // std::cout << source.VertexSource << std::endl;
    // std::cout << "FRAGMENT:" << std::endl;
    // std::cout << source.FragmentSource << std::endl;

    m_RendererID = CreateShader(source.VertexSource, source.FragmentSource);
	
}

Shader::~Shader()
{
    GLCall(glDeleteProgram(m_RendererID));
}

void Shader::Bind() const
{
    GLCall(glUseProgram(m_RendererID));
}

void Shader::Unbind() const
{
    GLCall(glUseProgram(0));
}


void Shader::SetUniformMat4f(const std::string& name, const glm::mat4& matrix)
{
	// 因为glm和OpenGL都是Column-Major的,所以不需要
	// 传输数据的指针,所以是 &matrix[0][0]
    GLCall(glUniformMatrix4fv(GetUniformLocation(name), 1, GL_FALSE, &matrix[0][0]));
}
void Shader::SetUniform4f(const std::string& name, float v0, float v1, float v2, float v3)
{
	
    int location = GetUniformLocation(name);
    GLCall(glUniform4f(location, v0, v1, v2, v3));
}

void Shader::SetUniform1i(const std::string&name, int value)
{
    int location = GetUniformLocation(name);
    GLCall(glUniform1i(location, value));
}

int Shader::GetUniformLocation(const std::string& name)
{
	if (location_cache.find(name) != location_cache.end())
	{
        return location_cache[name];
	}	
    GLCall(int location = glGetUniformLocation(m_RendererID, name.c_str()));
    if (location == -1) // 注意location是int型，因为可以返回负数，表示获取失败
    {
        std::cout << "Uniform not exist:" << name << std::endl;
    } else
    {
        location_cache[name] = location;
    }
    return location;
}

ShaderProgramSource Shader::ParseShader(const std::string& filePath)
{
    std::fstream stream(filePath);

    enum class ShaderType { NONE = -1, VERTEX = 0, FRAGMENT = 1 };
    ShaderType type = ShaderType::NONE;
    std::string line;
    std::stringstream ss[2];
    while (getline(stream, line))
    {
        if (line.find("#shader") != std::string::npos)
        {
            if (line.find("vertex") != std::string::npos)
            {
                type = ShaderType::VERTEX;
            }
            else if (line.find("fragment") != std::string::npos)
            {
                type = ShaderType::FRAGMENT;
            }
        }
        else
        {
            ss[(int)type] << line << "\n";
        }
    }
    return { ss[0].str(), ss[1].str() };
}

unsigned int Shader::CompileShader(unsigned int type, const std::string& source)
{
    unsigned int id = glCreateShader(type);
    const char* src = source.c_str();
    glShaderSource(id, 1, &src, nullptr);
    glCompileShader(id);
    int result;
    glGetShaderiv(id, GL_COMPILE_STATUS, &result);
    if (result == GL_FALSE)
    {
        int length;
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        char* message = (char*)alloca(length * sizeof(char));
        glGetShaderInfoLog(id, length, &length, message);
        std::cout << "Fail to compile " << ((type == GL_VERTEX_SHADER) ? "vertex" : "fragment") << " shader!" << std::endl;
        std::cout << message << std::endl;
        glDeleteShader(id);
        return 0;
    }
    return id;
}

unsigned int Shader::CreateShader(const std::string& verexShader, const std::string& fragmentShader)
{
    unsigned int program = glCreateProgram();
    unsigned int vs = CompileShader(GL_VERTEX_SHADER, verexShader);
    unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);
    glAttachShader(program, vs);
    glAttachShader(program, fs);
    glLinkProgram(program);
    glValidateProgram(program);

    glDeleteShader(vs);
    glDeleteShader(fs);
    // 讲道理你应该使用 glDetachShader这种自动删除的接口。。。
    return program;
}

